package cn.hzc.fortess.system.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hzc.fortess.core.ServletCtx;
import cn.hzc.fortess.core.common.BizErrorEnum;
import cn.hzc.fortess.core.common.annotion.CURDLog;
import cn.hzc.fortess.core.common.annotion.CURDType;
import cn.hzc.fortess.core.common.exception.ServiceException;
import cn.hzc.fortess.system.common.EnableStatus;
import cn.hzc.fortess.core.common.constant.Const;
import cn.hzc.fortess.core.shiro.ShiroKit;
import cn.hzc.fortess.system.common.MenuType;
import cn.hzc.fortess.system.common.ResourceScope;
import cn.hzc.fortess.system.common.VisibleStatus;
import cn.hzc.fortess.system.dao.MenuDao;
import cn.hzc.fortess.system.dao.ProjectDao;
import cn.hzc.fortess.system.dao.RoleDao;
import cn.hzc.fortess.system.entity.Menu;
import cn.hzc.fortess.system.entity.Project;
import cn.hzc.fortess.system.entity.Role;
import cn.hzc.fortess.system.node.MenuNode;
import cn.hzc.fortess.system.node.ZTreeNode;
import cn.hzc.fortess.system.page.Pagination;
import cn.hzc.fortess.system.search.MenuSearch;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class MenuServiceImpl implements MenuService {

    @Autowired
    MenuDao menuDao;
    @Autowired
    RoleDao roleDao;
    @Autowired
    ProjectDao projectDao;

    @Override
    public List<MenuNode> getIndexMenuNodes(Project project, List<Integer> roleIds) {
        List<MenuNode> menus = this.menuDao.getSamMenusByRoleIds(project.getId(), roleIds);
        if (CollectionUtils.isEmpty(menus)) {
            return Lists.newArrayList();
        }
        menus.stream().forEach(menuItem -> {
            if (StringUtils.isNotBlank(menuItem.getUrl()) && (!menuItem.getUrl().startsWith("http"))) {
                if (Objects.equals(Const.SAM_PROJECT_ID, project.getId())) {
                    menuItem.setUrl(ServletCtx.getContextPath() + menuItem.getUrl());
                } else {
                    menuItem.setUrl(ServletCtx.getContextPath() + "/sam/oss?service=" + project.getProjectDomain() + menuItem.getUrl());
                }
            } else if (menuItem.getUrl().startsWith("http")) {
                menuItem.setUrl(ServletCtx.getContextPath() + "/sam/oss?service=" + menuItem.getUrl());

            }
        });
        return MenuNode.buildTitle(menus);
    }

    @Override
    public List<MenuNode> getUserMenus(final String projectCode, final Integer userId) {
        List<Role> roles = roleDao.findByUserId(userId);
        if (CollectionUtils.isEmpty(roles)) {
            return Lists.newArrayList();
        }
        roles.removeIf(role -> Objects.equals(role.getStatus(), EnableStatus.DISABLE.getStatus())
                || role.getId() == Const.PROJECT_ADMIN_ROLE_ID
                || role.getId() == Const.ADMIN_ROLE_ID);
        if (CollectionUtils.isEmpty(roles)) {
            return Lists.newArrayList();
        }

        List<Integer> roleIds = roles.parallelStream().map(Role::getId).collect(Collectors.toList());

        final List<Project> projects = projectDao.findProjectByUserRole(roleIds);

        if (CollectionUtils.isEmpty(projects)) {
            return Lists.newArrayList();
        }
        final List<MenuNode> menus = Lists.newArrayList();
        projects.stream().forEach(project -> {
            if (Objects.equals(project.getId(), Const.SAM_PROJECT_ID)) {
                return;
            }
            if (StringUtils.isNotBlank(projectCode) && (!projectCode.equals(project.getProjectCode()))) {
                return;
            }
            List<MenuNode> projectMenus = this.menuDao.getSamMenusByRoleIds(project.getId(), roleIds);
            if (!CollectionUtils.isEmpty(projectMenus)) {
                menus.add(projectMenus.get(0));
            }
        });
        return MenuNode.buildTitle(menus);
    }

    @Override
    @CURDLog(value = "创建项目根菜单")
    public void createRootMenu4Project(Project entity) {
        Menu menu = new Menu();
        menu.setCode(entity.getProjectCode());
        menu.setParentId(Const.TREE_ROOT_PARENT_ID);
        menu.setPcode("0");
        menu.setPcodes(Const.TREE_ROOT_PARENT_IDS);
        menu.setResourceKey(UUID.randomUUID().toString().replace("-", ""));
        menu.setResourceName(entity.getProjectName());
        menu.setResourceType(MenuType.MENU.getType()); //菜单
        menu.setResourcePath("/");
        menu.setViewStatus(VisibleStatus.ENABLE.getStatus());
        menu.setProjectId(entity.getId());
        menu.setIdx(Const.DEFAULT_IDX);
        menu.setLevels(1);
        menu.setStatus(EnableStatus.ENABLE.getStatus());
        menu.setCreateTime(new Date());
        menu.setCreateUser(ShiroKit.getUser().getAccount());
        menuDao.insert(menu);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CURDLog(value = "创建菜单")
    public void add(Menu entity) {
        entity.setCode(ShiroKit.getRandomSalt() + entity.getPid());
        Menu menu = this.menuSetPcode(entity);
        if (menu.getLevels() > Const.LEVEL_MENU) {
            throw new ServiceException(BizErrorEnum.CANT_OUT_LEVEL);
        }
        menu.setParentId(entity.getPid());
        menu.setCreateTime(new Date());
        menu.setCreateUser(ShiroKit.getUser().getAccount());

        menu.setViewStatus(Objects.equals(MenuType.LINK.getType(), entity.getResourceType()) ? VisibleStatus.DISABLE.getStatus() : VisibleStatus.ENABLE.getStatus());
        buildMenuAuthIdent(menu);
        menuDao.insert(menu);

        this.updateResourceScope(menu);
    }

    private void buildMenuAuthIdent(Menu menu) {
        if (StringUtils.isBlank(menu.getAuthIdentification())) {
            return;
        }
        //权限标识必须为"项目名:"开头
        Project project = projectDao.getById(menu.getProjectId());
        final String authIdentPrefix = project.getProjectCode() + ":";
        if (!menu.getAuthIdentification().startsWith(authIdentPrefix)) {
            menu.setAuthIdentification(authIdentPrefix + menu.getAuthIdentification());
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    @CURDLog(value = "编辑菜单", curdType = CURDType.EDIT)
    public void update(Menu entity) {

        entity.setUpdateTime(new Date());
        entity.setUpdateUser(ShiroKit.getUser().getAccount());
        entity.setViewStatus(Objects.equals(MenuType.LINK.getType(), entity.getResourceType()) ? VisibleStatus.DISABLE.getStatus() : VisibleStatus.ENABLE.getStatus());
        buildMenuAuthIdent(entity);

        menuDao.update(entity);

        this.updateResourceScope(entity);

    }

    private void updateResourceScope(Menu menu) {

        if (Objects.equals(MenuType.MENU.getType(), menu.getResourceType())
                && StringUtils.isNotBlank(menu.getResourcePath())
        ) {
            final Integer resourceScope = menuDao.findScopeByResourceId(menu.getId());
            if (resourceScope == null) {
                //保存资源数据控制范围
                menuDao.insertResourceScope(menu.getId(), menu.getResourceScope());
            } else if (!Objects.equals(resourceScope, menu.getResourceScope())) {
                menuDao.updateResourceScope(menu.getId(), menu.getResourceScope());
            }
        }
    }
//

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CURDLog(value = "删除菜单", curdType = CURDType.DELETE)
    public void delete(Integer id) {
        this.menuDao.delete(id);
        this.menuDao.deleteRelationByMenu(id);
    }

    @Override
    @CURDLog(value = "删除项目所有菜单", curdType = CURDType.DELETE)
    public void deleteByProject(Integer projectId) {
        //查询项目的根菜单
        Integer rootMenuId = menuDao.getRootMenuIdByProjectId(projectId);
        if (rootMenuId != null) {
            delete(rootMenuId);
        }
    }

    @Override
    public Menu getById(Integer id) {
        return menuDao.findById(id);
    }

    @Override
    public Menu getByAuthIdent(String authIdent) {
        return menuDao.findByAuthIdent(authIdent);
    }

    @Override
    public Integer getResourceScopeById(Integer id) {
        Integer scope = menuDao.findScopeByResourceId(id);
        return scope == null ? ResourceScope.PUBLIC_READ_WRITE.getCode() : scope;
    }

    @Override
    public List<Menu> getMenuList(MenuSearch search) {

        List<Menu> menus = this.menuDao.selectMenuList(search);

        if (menus == null) {
            menus = new ArrayList<>();
        }

        //创建根节点
        Menu menu = new Menu();
        menu.setId(-1);
        menu.setResourceName("根节点");
        menu.setCode("0");
        menu.setPcode("-2");

        menus.add(menu);

        return menus;
    }

    @Override
    public List<Menu> getDataMenuList(Integer projectId) {
        List<Menu> menus = menuDao.findDataMenu(projectId);
        return menus == null ? Lists.newArrayList() : menus;
    }


    @Override
    public Pagination getPage(MenuSearch search) {
        Pagination pagination = new Pagination();
        pagination.setCount(menuDao.count(search));
        if (pagination.getCount() > 0) {
            pagination.setData(menuDao.page(search));
        }
        return pagination;
    }


    public Menu menuSetPcode(Menu menuParam) {
        Menu resultMenu = new Menu();
        BeanUtil.copyProperties(menuParam, resultMenu);
        if (Const.isTreeRoot(menuParam.getPid())) {
            resultMenu.setPcode("0");
            resultMenu.setPcodes("[0],");
        } else {
            Integer pid = menuParam.getPid();
            Menu pMenu = this.getById(pid);
            resultMenu.setPcode(pMenu.getCode());
            resultMenu.setPcodes(pMenu.getPcodes() + "[" + pMenu.getCode() + "],");
        }
        return resultMenu;
    }


    @Override
    public List<ZTreeNode> menuTreeList(List<Integer> projectIds) {
        if (CollectionUtils.isEmpty(projectIds)) {
            return Lists.newArrayList();
        }
        return this.menuDao.menuTreeList(projectIds);
    }

    @Override
    public List<Menu> getEnableAuthMenus(Integer projectId) {
        return menuDao.findEnableAuthMenus(projectId);
    }

    @Override
    public List<Integer> getMenuIdListByRole(Integer roleId) {
        return menuDao.findMenuIdListByRole(roleId);
    }

}
